#include "src/TGLine.h"


TGLine& TGLine::operator = (const TGLine& l) {
    data = l.data;
    return *this;
}

//求向量p13在关于向量p12的方向，>0位于逆时针，<0位于顺时针,=0则共线
double direction(const TGPoint& p1, const TGPoint& p2, const TGPoint& p3) {
    return xmul({p3.x-p1.x, p3.y-p1.y}, {p2.x-p1.x, p2.y-p1.y});
}

//判断位于直线p12上的点p3是否位于线段p1-p2
bool isColinePointOnSegment(const TGLine& l1, const TGPoint& p3) {

    auto& p1 = l1.data.first, &p2 = l1.data.second;
    if(std::min(p1.x, p2.x)<=p3.x && p3.x<=std::max(p1.x, p2.x) && std::min(p1.y, p2.y)<=p3.y && p3.y<=std::max(p1.y, p2.y)) {
        return true;
    }
    else {
        return false;
    }
}

//判断点p是否和线段l1共线
bool commonLine(const TGLine& l1, const TGPoint& p) {
    auto &p1 = l1.data.first, &p2 = l1.data.second;
    double d1 = (p.x-p1.x)*(p.y-p2.y);
    double d2 = (p.x-p2.x)*(p.y-p1.y);
    return dcmp(d1, d2) == 0;
}

int isIntersection(const TGLine& l1, const TGLine& l2) {
    auto &p1 = l1.data.first, &p2 = l1.data.second;
    auto &p3 = l2.data.first, &p4 = l2.data.second;

    double d1 = direction(p3, p4, p1);
    double d2 = direction(p3, p4, p2);
    double d3 = direction(p1, p2, p3);
    double d4 = direction(p1, p2, p4);

    // 判断是否相交以及是否有点在线段上
    if((d1*d2 < 0) && (d3*d4 < 0)) {
        return INTERSECTION;
    }
    else if(d1 == 0 && isColinePointOnSegment(l2, p1)) {
        return INTERSECTION;
    }
    else if(d2 == 0 && isColinePointOnSegment(l2, p2)) {
        return INTERSECTION;
    }
    else if(d3 == 0 && isColinePointOnSegment(l1, p3)) {
        return INTERSECTION;
    }
    else if(d4 == 0 && isColinePointOnSegment(l1, p4)) {
        return INTERSECTION;
    }
    else {
        return NOT;
    }

}

bool parallel(const TGLine& l1, const TGLine& l2) {
    auto &x1 = l1.data.first.x, &y1 = l1.data.first.y;
    auto &x2 = l1.data.second.x, &y2 = l1.data.second.y;
    auto &x3 = l2.data.first.x, &y3 = l2.data.first.y;
    auto &x4 = l2.data.second.x, &y4 = l2.data.second.y;
    return (y2-y1)*(x4-x3) == (y4-y3)*(x2-x1);
}

int commonLineAndIntersection(const TGLine& l1, const TGLine& l2) {
    // auto &p1 = l1.data.first, &p2 = l1.data.second;
    auto &p3 = l2.data.first, &p4 = l2.data.second;

    // 首先判断是否共线，然后判断是否交集
    if(commonLine(l1, p3) && commonLine(l1, p4)) {
        if(isColinePointOnSegment(l1, p3) || isColinePointOnSegment(l1, p4)) {
            return OVERLAP;
        }
        else {
            return COMMONLINE;
        }
    }
    else {
        return NOT;
    }
}

//判断线段l1和l2是否存在交点，如果存在交点则把交点存入点p
bool intersection(const TGLine& l1, const TGLine& l2, TGPoint& p) {
    auto &x1 = l1.data.first.x, &y1 = l1.data.first.y;
    auto &x2 = l1.data.second.x, &y2 = l1.data.second.y;
    auto &x3 = l2.data.first.x, &y3 = l2.data.first.y;
    auto &x4 = l2.data.second.x, &y4 = l2.data.second.y;

    //首先判断是否平行
    if(parallel(l1, l2)) {
        //std::cout << "parallel" << std::endl;
        //判断是否共线
        if(commonLineAndIntersection(l1, l2) == OVERLAP) {
            //如果存在多个相交点（l1和l2重合）,则输出最小(x,y)

            std::vector<TGPoint> tmp;
            tmp.reserve(4);
            if(isColinePointOnSegment(l2, l1.data.first)) {
                tmp.emplace_back(l1.data.first);
            }
            if(isColinePointOnSegment(l2, l1.data.second)) {
                tmp.emplace_back(l1.data.second);
            }
            if(isColinePointOnSegment(l1, l2.data.first)) {
                tmp.emplace_back(l2.data.first);
            }
            if(isColinePointOnSegment(l1, l2.data.second)) {
                tmp.emplace_back(l2.data.second);
            }
            //std::cout << tmp.size() << "\n";
            p = *std::min_element(tmp.begin(), tmp.end(), [](const TGPoint& p1, const TGPoint& p2){
                if(p1.x == p2.x) {
                    return p1.y < p2.y;
                }
                else {
                    return p1.x < p2.x;
                }
            });
            return true;
        }
        else {
            return false;
        }
    }
    else {
        /*
        根据参数方程计算相交点
        x = x1+t1*(x2-x1) y = y1+t1*(y2-y1)
        x = x3+t2*(x4-x3) y = y3+t2*(y4-y3)
        t1 = (x3-x1)(y4-y3)-(y3-y1)(x4-x3) / (x2-x1)(y4-y3)-(x4-x3)(y2-y1)
        t2 = -(x1-x3)(y2-y1)-(y3-y1)(x2-x1) / (x2-x1)(y4-y3)-(x4-x3)(y2-y1)
        判断t1,t2是否属于[0,1]，如果超出范围则表示相交点不在线段上
        */
        double d1 = (x3-x1)*(y4-y3)-(y3-y1)*(x4-x3);
        double d2 = (x2-x1)*(y4-y3)-(x4-x3)*(y2-y1);
        double d3 = -(x1-x3)*(y2-y1)-(y3-y1)*(x2-x1);
        double t1 = d1 / d2;
        double t2 = d3 / d2;
        if(0<=t1 && t1<=1 && 0<=t2 && t2<=1) {
            p.x = x1 + t1*(x2-x1);
            p.y = y1 + t1*(y2-y1);
            return true;
        }
        else {
            return false;
        }
    }
}

bool lineIntersect(const TGLine& l1, const TGLine& l2, TGPoint& p) {
    //返回值为0 说明两条直线平行
    auto &x1 = l1.data.first.x, &y1 = l1.data.first.y;
    auto &x2 = l1.data.second.x, &y2 = l1.data.second.y;
    auto &x3 = l2.data.first.x, &y3 = l2.data.first.y;
    auto &x4 = l2.data.second.x, &y4 = l2.data.second.y;
    double den =  (x1-x2) * (y3-y4) - (y1-y2) * (x3-x4);
    if(dcmp(den,0) == 0) return false;
    double numX = (x1*y2 - y1*x2) * (x3-x4) - (x1-x2) * (x3*y4 - y3*x4);
    double numY = (x1*y2 - y1*x2) * (y3-y4) - (y1-y2) * (x3*y4 - y3*x4);\
    p.x = numX/den;
    p.y = numY/den;
    return true;
}


std::pair<TGPoint, double> pdist(const TGPoint& p, const TGLine& line) {
    const double &x1 = line.data.first.x, &y1 = line.data.first.y;
    const double &x2 = line.data.second.x, &y2 = line.data.second.y;
    std::pair<TGPoint, double> ans;
    /*
    首先判断，点p关于直线的垂足是否位于线段上
    若垂足在线段外则∠12p和∠21p有一个为钝角否则都为锐角，钝角锐角判断可通过点积判断（小于0钝角大于0锐角等于0则为直角）
    若在线段外，则点到线段的距离为点到线段端点距离的最小值。若在线段内则可以通过叉积求三角形面积然后除以线段长度求点p到线段的距离
    */
    TGPoint v1(p.x-x1, p.y-y1), v2(x2-x1, y2-y1), v3(p.x-x2, p.y-y2);

    double d1 = v1 * v2;
    double d2 = v3 * (-v2);
    
    //位于线段外面
    if(d1 < 0 || d2 < 0) {
        d1 = pdist(p, line.data.first), d2 = pdist(p, line.data.second);
        if (d1 < d2) {
            ans.first = line.data.first;
            ans.second = d1;
        }
        else {
            ans.first = line.data.second;
            ans.second = d2;
        }
    }
    else {
        double area = fabs(v1^v2);
        double d = pdist(line.data.first, line.data.second);
        ans.second = area / d;
        // std::cout << area << " " << d << " " << ans.second << std::endl;
        d1 = (p.x-x1)*(x2-x1) + (p.y-y1)*(y2-y1);
        d2 = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1);
        d = d1 / d2;
        ans.first.x = x1 + d*(x2-x1);
        ans.first.y = y1 + d*(y2-y1);
    }

    return ans;
}

std::pair<TGPoint, double> pdist(const TGPoint& p, std::vector<TGPoint>& pts) {
    std::pair<TGPoint, double> ret;
    ret.second = INT32_MAX;
    //计算离定点最近的折线段距离
    int n = pts.size();
    for(int i = 1; i < n; i ++) {
        auto tmp = pdist(p, {pts[i-1], pts[i]});
        //cout << tmp.second << endl;
        if(tmp.second < ret.second) {
            ret = tmp;
        }
    }
    return ret;
}